/**
 * Copyright (c) 2006 eMundo GmbH. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * Project: agil-client-web
 * File: GwtRemoteServiceImpl.java
 *
 */

package eu.emundo.agilpro.web.server;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import eu.emundo.agilpro.fw.AdapterClassLoader;
import eu.emundo.agilpro.fw.AdapterFactoryImpl;
import eu.emundo.agilpro.fw.ProcessContext;
import eu.emundo.agilpro.fw.agex.Agex;
import eu.emundo.agilpro.fw.agex.AgexUiOptions;
import eu.emundo.agilpro.fw.dio.model.GenericModel;
import eu.emundo.agilpro.fw.intf.AdapterFactory;
import eu.emundo.agilpro.fw.intf.IDataObject;
import eu.emundo.agilpro.fw.intf.IImplementation;
import eu.emundo.agilpro.ifw.service.ejb.remote.Client;
import eu.emundo.agilpro.web.client.GwtRemoteService;
import eu.emundo.agilpro.web.client.dto.GwtParameterDTO;

/**
 * This class should be the connection between the GWT-Client and the Client-EJB
 * 
 * @author Fabian Wohlschl\u00E4ger
 * 
 */
public class GwtRemoteServiceImpl extends RemoteServiceServlet implements GwtRemoteService {
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = -2346628008106813192L;

	/**
	 * Logger
	 */
	private final static Logger sLogger = Logger.getLogger(GwtRemoteServiceImpl.class.getName());

	/**
	 * True if application is runned in hosted mode
	 */
	private boolean mTestMode = false;

	/**
	 * GwtToClientEjbProxy key in HttpSession
	 * Proxy is used for invoking the ClientEjb
	 */
	private static final String GWT_TO_CLIENT_EJB_PROXY = "GwtToClientEjbProxy";
	
	/**
	 * ClientToEjbRole key in HttpSession 
	 * Used for the data exchange from the
	 * GWT-client to the Client-EJB (GWT can not serialize Object)
	 */
	private static final String CLIENT_TO_EJB_ROLE_KEY = "ClientToEjbRole";

	/**
	 * EjbToClientRole key in HttpSession 
	 * Used for the data exchange from the
	 * Client-EJB to the GWT-client (GWT can not serialize Object)
	 */
	private static final String EJB_TO_CLIENT_ROLE_KEY = "EjbToClientRole";

	/**
	 * ClientToEjbActivity key in HttpSession 
	 * Used for the data exchange from
	 * the GWT-client to the Client-EJB (GWT can not serialize Object)
	 */
	private static final String CLIENT_TO_EJB_ACTIVITY_KEY = "ClientToEjbActivity";

	/**
	 * EjbToClientActivity key in HttpSession 
	 * Used for the data exchange from
	 * the Client-EJB to the GWT-client (GWT can not serialize Object)
	 */
	private static final String EJB_TO_CLIENT_ACTIVITY_KEY = "EjbToClientActivity";

	/**
	 * ClientToEjbDelayedActivity key in HttpSession 
	 * Used for the data exchange
	 * from the GWT-client to the Client-EJB (GWT can not serialize Object)
	 */
	private static final String CLIENT_TO_EJB_DELAYED_ACTIVITY_KEY = "ClientToEjbDelayedActivity";

	/**
	 * EjbToClientDelayedActivity key in HttpSession
	 * Used for the data exchange
	 * from the Client-EJB to the GWT-client (GWT can not serialize Object)
	 */
	private static final String EJB_TO_CLIENT_DELAYED_ACTIVITY_KEY = "EjbToClientDelayedActivity";

	/**
	 * ProcessContext key in HttpSession
	 */
	private static final String PC_KEY = "Pc";

	/**
	 * AdapterFactory key in HttpSession
	 */
	private static final String ADAPTER_FAC_KEY = "AdapterFac";

	/**
	 * ActivityStartedFlag key in HttpSession
	 * Should be true if one activity is
	 * started
	 */
	private static final String ACTIVITY_STARTED_FLAG_KEY = "ActivityStartedFlag";

	/**
	 * StopPollingFlag key in HttpSession
	 * True if the polling should be stopped
	 */
	private static final String STOP_POLLING_FLAG_KEY = "StopPollingFlag";

	
	
	/* (non-Javadoc)
	 * @see javax.servlet.GenericServlet#init()
	 */
	@Override
	public void init() throws ServletException {
		super.init();
		
		sLogger.debug("Test if the application is runned in hosted-mode");

		// If application is runned in test-mode only test-content is generated
		if (this.getProperty("agil.client.web.testmodus").equals("true")) {
			mTestMode = true;
		}
	}

	/**
	 * Should be the first method to call
	 */
	public void initConnection() {
		sLogger.debug("Creating session");
		
		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		sLogger.debug("Initialization of session variables");

		lSession.setAttribute(CLIENT_TO_EJB_ROLE_KEY, new HashMap<String, Object>());
		lSession.setAttribute(EJB_TO_CLIENT_ROLE_KEY, new HashMap<Object, String>());
		lSession.setAttribute(CLIENT_TO_EJB_ACTIVITY_KEY, new HashMap<String, Object>());
		lSession.setAttribute(EJB_TO_CLIENT_ACTIVITY_KEY, new HashMap<Object, String>());
		lSession.setAttribute(CLIENT_TO_EJB_DELAYED_ACTIVITY_KEY, new HashMap<String, Object>());
		lSession.setAttribute(EJB_TO_CLIENT_DELAYED_ACTIVITY_KEY, new HashMap<Object, String>());
		lSession.setAttribute(PC_KEY, null);
		lSession.setAttribute(ACTIVITY_STARTED_FLAG_KEY, false);
		lSession.setAttribute(STOP_POLLING_FLAG_KEY, false);
		
		if (!mTestMode) {
			// Initializing AdapterFactory
			lSession.setAttribute(ADAPTER_FAC_KEY, AdapterFactoryImpl.getInstance());
			try {
				this.initAdapterFactory();
			} catch (Exception e) {
				sLogger.warn("Couldn't initialize AdapterFactory - " + e);
			}

			// Initializing ClientEjb
			try {
				// ClientConfiguration
				Properties lProp = new Properties();

				lProp.put("java.naming.factory.initial", this
						.getProperty(GwtRemoteServiceConstants.PROPERTY_JAVA_NAMING_FACTORY_INITIAL));
				lProp.put("java.naming.factory.url.pkgs", this
						.getProperty(GwtRemoteServiceConstants.PROPERTY_JAVA_NAMING_FACTORY_URL_PKGS));
				lProp.put("java.naming.provider.url", this
						.getProperty(GwtRemoteServiceConstants.PROPERTY_JAVA_NAMING_PROVIDER_URL));
				lProp.put("com.sun.jndi.dns.timeout.initial", this
						.getProperty(GwtRemoteServiceConstants.PROPERTY_COM_SUN_JNDI_DNS_TIMEOUT_INITIAL));
				lProp.put("com.sun.jndi.dns.timeout.retries", this
						.getProperty(GwtRemoteServiceConstants.PROPERTY_COM_SUN_JNDI_DNS_TIMEOUT_RETRIES));
				lProp.put("jnp.connect.timeout", this
						.getProperty(GwtRemoteServiceConstants.PROPERTY_JNP_CONNECT_TIMEOUT));
				lProp.put("jnp.sotimeout", this.getProperty(GwtRemoteServiceConstants.PROPERTY_JNP_SOTIMEOUT));
				InitialContext lCtx = new InitialContext(lProp);
				Client lClientEjb = (Client) PortableRemoteObject.narrow(lCtx.lookup("agilpro/Client"), Client.class);
				GwtToClientEjbProxy lProxy = new GwtToClientEjbProxy(lClientEjb);
				this.setGwtToClientEjbProxy(lProxy);
			} catch (Exception e) {
				sLogger.warn("Couldn't connect to ClientEjb - " + e);
			}
		}
	}

	/**
	 * This one initialises the adapter factory by loading an external file
	 * 
	 * @throws Exception
	 */
	private void initAdapterFactory() throws Exception {
		if (!mTestMode) {
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			
			// set configuration xml file to factory
			File lConfigFile = this.getFile(this.getProperty("afw.config.file"));
			AdapterFactory lAdapterFac = (AdapterFactory) lSession.getAttribute(ADAPTER_FAC_KEY);
			lAdapterFac.setConfigurationFile(lConfigFile);
			sLogger.info("Initialization of the AdapterFactory: Setted config-file " + lConfigFile);

			// set configuration path to factory where adapter properties are
			// stored
			File lConfigPath = this.getFile(this.getProperty("afw.config.path"));
			lAdapterFac.setConfigurationPath(lConfigPath);
			sLogger.info("Initialization of the AdapterFactory: Setted config-path " + lConfigPath);

			// Set internal AdapterPath to adapter factory
			File lAdapterPath = this.getFile(this.getProperty("adapter.path"));
			lAdapterFac.setInternalAdapterPath(lAdapterPath);
			sLogger.info("Initialization of the AdapterFactory: Setted adapter-path " + lAdapterPath);

			// getting jars of adapter path
			List<File> lAdapterPaths = lAdapterFac.getAdapterPaths();
			URL[] jars = getJarsOfDirectories(lAdapterPaths);
			sLogger.info("Initialization of the AdapterFactory: Got jars which are located in the adapter-path"
					+ lAdapterPath);

			lAdapterFac.addJarsToClassLoader(jars);
			sLogger.info("Initialization of the AdapterFactory: Loaded adapters");

			// initialize adapter factory
			lAdapterFac.init();
			lSession.setAttribute(ADAPTER_FAC_KEY, lAdapterFac);
			sLogger.info("Initialization of the AdapterFactory: Completed");
		}
	}

	/**
	 * This method search for jars in a direcotry
	 * 
	 * @param pDirectories
	 *            the directory to search
	 * @return
	 */
	private URL[] getJarsOfDirectories(List<File> pDirectories) {
		List<URL> lTmpJarUrls = new LinkedList<URL>();

		for (File lDirectory : pDirectories) {
			File lDir = lDirectory;
			sLogger.info("searching for jars in " + lDir.getAbsolutePath());
			if (lDir.isDirectory()) {
				// define a file list filer for directory
				File[] lJars = lDir.listFiles(new FileFilter() {
					public boolean accept(File pathname) {
						if (pathname.getName().endsWith(".jar") || pathname.getName().endsWith(".properties"))
							return true;
						else
							return false;
					}
				});

				// adding jars in an url list
				for (File lJar : lJars) {
					try {
						lTmpJarUrls.add(lJar.toURL());
						sLogger.info("found library " + lJar.getAbsolutePath());
					} catch (MalformedURLException e) {
						sLogger.info("could not load library " + lJar.getAbsolutePath());
					}
				}
			}
		}

		// Convert List to Array
		URL[] lJarUrls = new URL[lTmpJarUrls.size()];
		lTmpJarUrls.toArray(lJarUrls);

		return lJarUrls;
	}

	/**
	 * Cancels the current action
	 * 
	 * (non-Javadoc)
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#cancelAction()
	 */
	public void cancelAction() {
		sLogger.debug("Canceling Action");
		
		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		boolean lActivityStartedFlag = ((Boolean) lSession.getAttribute(ACTIVITY_STARTED_FLAG_KEY)).booleanValue();
		if ((!mTestMode) && (lActivityStartedFlag == true)) {
			lSession.setAttribute(STOP_POLLING_FLAG_KEY, true);

			resetActivityExecution();

			lSession.setAttribute(ACTIVITY_STARTED_FLAG_KEY, false);
		}
	}

	/**
	 * Changes the role
	 * 
	 * @param pNewRole
	 *            newrole
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#changedRole(java.lang.String)
	 */
	public void changedRole(String pNewRole) {
		sLogger.debug("Changing role to " + pNewRole);

		if (!mTestMode) {
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			Map lClientToEjbRole = (Map) lSession.getAttribute(CLIENT_TO_EJB_ROLE_KEY);
			this.getGwtToClientEjbProxy().changedRole(lClientToEjbRole.get(pNewRole));
		}
	}

	/**
	 * Gets the activities for one role
	 * 
	 * @param pRole
	 *            role
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getActiviesForRole(java.lang.String)
	 */
	public String[] getActivitiesForRole(String pRole) {
		sLogger.debug("Getting activities for role " + pRole);

		if (mTestMode) {
			String[] lActivities = { "activity1", "activity2", "activity3" };

			return lActivities;
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		// Clear activities
		Map lClientToEjbActivity = (Map) lSession.getAttribute(CLIENT_TO_EJB_ACTIVITY_KEY);
		lClientToEjbActivity.clear();
		Map lEjbToClientActivity = (Map) lSession.getAttribute(EJB_TO_CLIENT_ACTIVITY_KEY);
		lEjbToClientActivity.clear();

		// Get acitivies
		Map lClientToEjbRole = (Map) lSession.getAttribute(CLIENT_TO_EJB_ROLE_KEY);
		Object lRole = lClientToEjbRole.get(pRole);
		Object[] lActivities = this.getGwtToClientEjbProxy().getActivitiesForRole(lRole);
		
		/*
		 * For the data exchange from the GWT-client to the Client-EJB (GWT can
		 * not serialize Object)
		 */
		for (Object lActivity : lActivities) {
			lClientToEjbActivity.put(lActivity.toString(), lActivity);
			lEjbToClientActivity.put(lActivity, lActivity.toString());
		}
		lSession.setAttribute(CLIENT_TO_EJB_ACTIVITY_KEY, lClientToEjbActivity);
		lSession.setAttribute(EJB_TO_CLIENT_ACTIVITY_KEY, lEjbToClientActivity);

		return objectArrayToStringArray(lActivities);
	}

	/**
	 * Gets the delayed activities for one role
	 * 
	 * @param pRole
	 *            role
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getActiviesForRole(java.lang.String)
	 */
	public String[] getDelayedActivitesForRole(String pRole) {
		sLogger.debug("Getting delayed activities for role " + pRole);

		if (mTestMode) {
			String[] lDelayedActivities = { "delayedActivity1", "delayedActivity2", "delayedActivity3" };

			return lDelayedActivities;
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		// Get delayed activities
		Map lClientToEjbRole = (Map) lSession.getAttribute(CLIENT_TO_EJB_ROLE_KEY);
		Object lRole = lClientToEjbRole.get(pRole);
		Object[] lDelayedActivities = this.getGwtToClientEjbProxy().getDelayedActivitiesForRole(lRole);
		
		/*
		 * For the data exchange from the GWT-client to the Client-EJB (GWT can
		 * not serialize Object)
		 */
		Map lClientToEjbDelayedActivity = (Map) lSession.getAttribute(CLIENT_TO_EJB_DELAYED_ACTIVITY_KEY);
		Map lEjbToClientDelayedActivity = (Map) lSession.getAttribute(EJB_TO_CLIENT_DELAYED_ACTIVITY_KEY);
		for (Object lDelayedActivity : lDelayedActivities) {
			lClientToEjbDelayedActivity.put(lDelayedActivity.toString(), lDelayedActivity);
			lEjbToClientDelayedActivity.put(lDelayedActivity, lDelayedActivity.toString());
		}
		lSession.setAttribute(CLIENT_TO_EJB_DELAYED_ACTIVITY_KEY, lClientToEjbDelayedActivity);
		lSession.setAttribute(EJB_TO_CLIENT_DELAYED_ACTIVITY_KEY, lEjbToClientDelayedActivity);

		return objectArrayToStringArray(lDelayedActivities);
	}

	/**
	 * Gets the roles
	 * 
	 * @return Roles
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getRoles()
	 */
	public String[] getRoles() {
		sLogger.debug("Getting roles");

		if (mTestMode) {
			String[] lRoles = { "role1", "role2", "role3" };

			return lRoles;
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		// Clear roles
		Map lClientToEjbRole = (Map) lSession.getAttribute(CLIENT_TO_EJB_ROLE_KEY);
		lClientToEjbRole.clear();

		Map lEjbToClientRole = (Map) lSession.getAttribute(EJB_TO_CLIENT_ROLE_KEY);
		lEjbToClientRole.clear();

		// Get roles
		Object[] lRoles = this.getGwtToClientEjbProxy().getRoles();
		
		/*
		 * For the data exchange from the GWT-client to the Client-EJB (GWT can
		 * not serialize Object)
		 */
		for (Object lRole : lRoles) {
			lClientToEjbRole.put(lRole.toString(), lRole);
			lEjbToClientRole.put(lRole, lRole.toString());
		}
		lSession.setAttribute(CLIENT_TO_EJB_ROLE_KEY, lClientToEjbRole);
		lSession.setAttribute(EJB_TO_CLIENT_ROLE_KEY, lEjbToClientRole);

		return objectArrayToStringArray(lRoles);
	}

	/**
	 * Gets the url, which should be displayed at the client
	 * 
	 * @return null if a GenericUI should be displayed, otherwise the url is
	 *         returned
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getUrl()
	 */
	public String getUrl() {
		sLogger.debug("Getting URL");

		if (mTestMode) {
			Random lRnd = new Random();

			/*
			 * Generating random-number between 0 and 50. If the number is
			 * divisible by 2, a url is returned. Otherwise null is returned.
			 */
			if (((Math.abs(lRnd.nextInt()) % 50) % 2) == 0) {
				return "http://www.google.de";
			}

			return null;
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		String lUrl = null;
		lSession.setAttribute(STOP_POLLING_FLAG_KEY, false);

		// Polling the ProcessContext
		AdapterFactory lAdapterFac = (AdapterFactory) lSession.getAttribute(ADAPTER_FAC_KEY);
		byte[] lPcSerialized = null;
		ProcessContext lPc = null;

		try {
			while (((lPcSerialized == null) || (lPc == null))
					&& (!((Boolean) lSession.getAttribute(STOP_POLLING_FLAG_KEY)).booleanValue())) {
				// Get ProcessContext
				lPcSerialized = this.getGwtToClientEjbProxy().getProcessContext();

				// If there is a new ProcessContext
				if (lPcSerialized != null) {
					lPc = ProcessContext.deserialize(lPcSerialized);
					if (lPc != null) {
						sLogger.info("Interpret ProcessContext");

						// If no GenericUi should be displayed
						String lCanNameGenericUI = this
								.getProperty(GwtRemoteServiceConstants.PROPERTY_CANONICAL_NAME_GENERICUI);
						if (!lPc.getApplicationClass().equals(lCanNameGenericUI)) {
							try {
								AdapterClassLoader lClassloader = lAdapterFac.getClassLoader();
								sLogger.debug("Loading applocation class " + lPc.getApplicationClass());
								Class lApp = lClassloader.loadClass(lPc.getApplicationClass());

								lAdapterFac.setProcessContext(lPc);
								IImplementation lCurrentAdapterIntf = lAdapterFac.getAgilProInterface(lApp
										.getCanonicalName());
								Method[] lIntfMethods = lCurrentAdapterIntf.getClass().getMethods();
								for (Method lIntfMethod : lIntfMethods) {
									if (lIntfMethod.getName().compareToIgnoreCase("setdto") == 0) {
										try {
											// Invoke setDto in adapter
											Number lDtoNumber = lPc.getApplicationClassDtoNumber();
											IDataObject lDto = lPc.getDataObject(lDtoNumber);
											lIntfMethod.invoke(lCurrentAdapterIntf, lDto);

											// Invoke getUrl in adapter
											lUrl = lAdapterFac.getUrl();

											break;
										} catch (IllegalArgumentException e) {
											sLogger.warn("Couldn't interpret ProcessContext - " + e);
											throw new Exception("Couldn't interpret ProcessContext - " + e);
										} catch (InvocationTargetException e) {
											sLogger.warn("Couldn't interpret ProcessContext, because the method "
													+ lIntfMethod.getName() + " couldn't be invoked - " + e);
											throw new Exception("Couldn't interpret ProcessContext - " + e);
										}
									}
								}
							} catch (ClassNotFoundException e) {
								sLogger.warn("Couldn't interpret ProcessContext, because the adapter "
										+ lPc.getApplicationClass() + " couldn't be found");
								throw new Exception("Couldn't interpret ProcessContext, because the adapter "
										+ lPc.getApplicationClass() + " couldn't be found");
							} catch (Exception e) {
								sLogger.warn("Couldn't interpret ProcessContext - " + e);
								throw new Exception("Couldn't interpret ProcessContext - " + e);
							}
						}
					} else {
						Thread.sleep(100);
					}
				} else {
					Thread.sleep(100);
				}
			}
		} catch (Exception e) {
			sLogger.warn("Could not return URL - " + e);
		}

		// Set AdapterFactory in HttpSession
		lSession.setAttribute(ADAPTER_FAC_KEY, lAdapterFac);

		// Set Process Context in HttpSession
		lSession.setAttribute(PC_KEY, lPc);

		return lUrl;
	}

	/**
	 * Performs next action
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#nextAction()
	 */
	public void nextAction() {
		sLogger.debug("Performing next action");

		if (!mTestMode) {
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			lSession.setAttribute(STOP_POLLING_FLAG_KEY, true);
			String lCanNameGenericUI = this.getProperty(GwtRemoteServiceConstants.PROPERTY_CANONICAL_NAME_GENERICUI);
			ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);
			boolean lActivityStartedFlag = ((Boolean) lSession.getAttribute(ACTIVITY_STARTED_FLAG_KEY)).booleanValue();
			if (lActivityStartedFlag) {
				try {
					if (lActivityStartedFlag && (!lPc.getApplicationClass().equals(lCanNameGenericUI))) {
						AdapterFactory lAdapterFac = (AdapterFactory) lSession.getAttribute(ADAPTER_FAC_KEY);

						// Invoke stop in adapter
						while (true) {
							if (lAdapterFac.canStop()) {
								lAdapterFac.stop();
								break;
							}
						}

						lSession.setAttribute(ADAPTER_FAC_KEY, lAdapterFac);
					}

					// Set ProcessContext
					this.getGwtToClientEjbProxy().setProcessContext(lPc.serialize());
					
					// Invoke next action
					this.getGwtToClientEjbProxy().nextAction();
					
					lSession.setAttribute(STOP_POLLING_FLAG_KEY, false);
				} catch (IOException e) {
					sLogger.warn("Couldn't serialize ProcessContext - " + e);
				} catch (Exception e) {
					sLogger.warn("Couldn't invoke next action - " + e);
				}
			}
		}
	}

	/**
	 * Delays the current action
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#delayAction()
	 */

	public void delayAction() {
		sLogger.debug("Delaying action");

		if (!mTestMode) {
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			
			lSession.setAttribute(STOP_POLLING_FLAG_KEY, true);

			this.resetActivityExecution();

			// Delay action
			this.getGwtToClientEjbProxy().delayAction();
		}
	}

	/**
	 * Starts an activity
	 * 
	 * @param pActivityName
	 *            Activity
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#startActity(java.lang.String)
	 */
	public void startActivity(String pActivityName) {
		sLogger.debug("Starting activity " + pActivityName);

		if (!mTestMode) {
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			
			lSession.setAttribute(STOP_POLLING_FLAG_KEY, true);

			boolean lActivityStartedFlag = ((Boolean) lSession.getAttribute(ACTIVITY_STARTED_FLAG_KEY)).booleanValue();
			if (lActivityStartedFlag) {
				this.cancelAction();
			}

			// Generate new ticketnumber
			this.getGwtToClientEjbProxy().newTicketNumber();

			// Start activity
			Map lClientToEjbActivity = (Map)lSession.getAttribute(CLIENT_TO_EJB_ACTIVITY_KEY);
			Object lActivity = lClientToEjbActivity.get(pActivityName);
			this.getGwtToClientEjbProxy().startActivity(lActivity);
			
			lSession.setAttribute(ACTIVITY_STARTED_FLAG_KEY, true);
		}
	}

	/**
	 * Resumes an delayed activity
	 * 
	 * @param pDelayedActivityName
	 *            delayed activity
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#resumeDelayedActivity(java.lang.String)
	 */

	public void resumeDelayedActivity(String pDelayedActivityName) {
		sLogger.debug("Resuming delayed activity " + pDelayedActivityName);

		if (!mTestMode) {
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			
			lSession.setAttribute(STOP_POLLING_FLAG_KEY, true);

			boolean lActivityStartedFlag = ((Boolean) lSession.getAttribute(ACTIVITY_STARTED_FLAG_KEY)).booleanValue();
			if (lActivityStartedFlag) {
				this.cancelAction();
			}

			// Resume delayed activity
			Map lClientToEjbDelayedActivity = (Map) lSession.getAttribute(CLIENT_TO_EJB_DELAYED_ACTIVITY_KEY);
			Object lDelayedActivity = lClientToEjbDelayedActivity.get(pDelayedActivityName);			
			this.getGwtToClientEjbProxy().resumeDelayedActivity(lDelayedActivity);
			
			lSession.setAttribute(ACTIVITY_STARTED_FLAG_KEY, true);
		}
	}

	/**
	 * Gets the active action
	 * 
	 * @return Active action
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getActiveAction()
	 */
	public String getActiveAction() {
		sLogger.debug("Getting active action");

		if (mTestMode) {
			return "activeAction";
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);
		String lActiveAction = null;
		if (lPc != null) {
			lActiveAction = lPc.getActiveAction();
		}

		return (lActiveAction == null) ? "" : lActiveAction;
	}

	/**
	 * Gets the active activity
	 * 
	 * @return Active activity
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getActiveActivity()
	 */
	public String getActiveActivity() {
		sLogger.debug("Getting active activity");

		if (mTestMode) {
			return "activeActivity";
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);
		String lActiveActivity = null;
		if (lPc != null) {
			lActiveActivity = lPc.getActiveActivity();
		}

		return (lActiveActivity == null) ? "" : lActiveActivity;
	}

	/**
	 * Gets the active role
	 * 
	 * @return Active role
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getActiveRole()
	 */
	public String getActiveRole() {
		sLogger.debug("Getting active role");

		if (mTestMode) {
			return "activeRole";
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);
		String lActiveRole = null;
		if (lPc != null) {
			lActiveRole = lPc.getActiveRole();
		}

		return (lActiveRole == null) ? "" : lActiveRole;
	}

	/**
	 * Converts an Object-array to a String-array
	 * 
	 * @param pObjectArray
	 *            Array with Object-values
	 * @return Array with String-values
	 */
	private String[] objectArrayToStringArray(Object[] pObjectArray) {
		sLogger.debug("Converting Object-array to String-array");

		int lObjectArrayLength = pObjectArray.length;
		String[] lStringArray = new String[lObjectArrayLength];

		for (int lI = 0; lI < lObjectArrayLength; lI++) {
			lStringArray[lI] = pObjectArray[lI].toString();
		}

		return lStringArray;
	}

	private GwtParameterDTO getTestParameters() {
		HashMap<String, String[]> lParamMap = new HashMap<String, String[]>();

		String[] param1 = { "value1" };

		lParamMap.put("param1", param1);

		String[] param2 = { "value1", "value2" };

		lParamMap.put("param2", param2);

		GwtParameterDTO lDto = new GwtParameterDTO();
		lDto.setParameters(lParamMap);

		return lDto;
	}

	/**
	 * Gets the parameters of an action
	 * 
	 * @return Parameters
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getParameters()
	 */
	@SuppressWarnings("unchecked")
	public GwtParameterDTO getParameters() {
		sLogger.debug("Getting parameters");

		if (mTestMode) {
			return this.getTestParameters();
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);
		if(lPc == null)	{
			return null;
		}
		GenericModel lDioModel = (GenericModel) lPc.getGenericDio();
		HashMap<String, String[]> lParamMap = new HashMap<String, String[]>();

		for (int lI = 0; lI < lDioModel.getEntryCount(); lI++) {
			// getting name and value of dio
			String lEntryName = lDioModel.getEntryNameAt(lI).toString();
			String lEntryValue = lDioModel.getEntryValueAt(lI).toString();

			// if dio value contains pipe
			if (lEntryValue.contains(this.getProperty(GwtRemoteServiceConstants.PROPERTY_SEPARATOR_COMBO))) {
				// parse the value with the agex parser
				Agex lExp = Agex.parse(lEntryValue);
				if (lExp instanceof AgexUiOptions) {
					// parsing result is AgexUiOptions create combo box
					// typecasting to AgexUiOptions
					AgexUiOptions lExpUi = ((AgexUiOptions) lExp);

					// creating array for list values of AgexUiOptions
					String[] lValues = new String[lExpUi.getList().size()];

					// copy values to array String
					lExpUi.getList().toArray(lValues);

					lParamMap.put(lEntryName, lValues);
				}
			} else {
				String[] lValues = { lEntryValue };
				lParamMap.put(lEntryName, lValues);
			}
		}

		GwtParameterDTO lDto = new GwtParameterDTO();
		lDto.setParameters(lParamMap);

		return lDto;
	}

	/**
	 * Gets the parameters of an action
	 * 
	 * @return Parameters
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#getParameters()
	 */
	@SuppressWarnings("unchecked")
	public GwtParameterDTO getVisibleParameters() {
		sLogger.debug("Getting parameters");

		if (mTestMode) {
			return this.getTestParameters();
		}

		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		HttpSession lSession = this.getThreadLocalRequest().getSession();
		
		ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);
		if(lPc == null)	{
			return null;
		}
		GenericModel lDioModel = (GenericModel) lPc.getGenericDio();
		HashMap<String, String[]> lParamMap = new HashMap<String, String[]>();

		for (int lI = 0; lI < lDioModel.getEntryCount(); lI++) {
			if (lDioModel.getEntryVisibility(lI)) {
				// getting name and value of dio
				String lEntryName = lDioModel.getEntryNameAt(lI).toString();
				String lEntryValue = lDioModel.getEntryValueAt(lI).toString();

				// if dio value contains pipe
				if (lEntryValue.contains(this.getProperty(GwtRemoteServiceConstants.PROPERTY_SEPARATOR_COMBO))) {
					// parse the value with the agex parser
					Agex lExp = Agex.parse(lEntryValue);
					if (lExp instanceof AgexUiOptions) {
						// parsing result is AgexUiOptions create combo box
						// typecasting to AgexUiOptions
						AgexUiOptions lExpUi = ((AgexUiOptions) lExp);

						// creating array for list values of AgexUiOptions
						String[] lValues = new String[lExpUi.getList().size()];

						// copy values to array String
						lExpUi.getList().toArray(lValues);

						lParamMap.put(lEntryName, lValues);
					}
				} else {
					String[] lValues = { lEntryValue };
					lParamMap.put(lEntryName, lValues);
				}
			}
		}
		GwtParameterDTO lDto = new GwtParameterDTO();
		lDto.setParameters(lParamMap);

		return lDto;
	}

	/**
	 * Sets the parameters of an action
	 * 
	 * @param pDto
	 *            parameters
	 * 
	 * @see eu.emundo.agilpro.web.client.GwtRemoteService#setParameters(eu.emundo.agilpro.web.client.dto.GwtParameterDTO)
	 */
	public void setParameters(GwtParameterDTO pDto) {
		sLogger.debug("Setting parameters");

		if (!mTestMode) {
			HashMap<String, String[]> lParamMap = pDto.getParameters();
			
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);
			GenericModel lDioModel = (GenericModel) lPc.getGenericDio();

			for (String lParamKey : lParamMap.keySet()) {
				for (int lI = 0, lIMax = lDioModel.getEntryCount(); lI < lIMax; lI++) {
					if (lDioModel.getEntryNameAt(lI).equals(lParamKey)) {
						lDioModel.setEntryValueAt(lI, lParamMap.get(lParamKey)[0]);
						lDioModel.setEntryVisibility(lI, false);
					}
				}
			}
			lSession.setAttribute(PC_KEY, lPc);
		}
	}

	/**
	 * Resets the execution of an activity
	 */
	private void resetActivityExecution() {
		if (!mTestMode) {
			String lCanNameGenericUI = this.getProperty(GwtRemoteServiceConstants.PROPERTY_CANONICAL_NAME_GENERICUI);
			
			/* 
			 * Only one instance of the servlet is created for all clients.
			 * HttpSession is used to store client specific data
			 */
			HttpSession lSession = this.getThreadLocalRequest().getSession();
			ProcessContext lPc = (ProcessContext) lSession.getAttribute(PC_KEY);

			// Invoke stop and exit in adapter
			boolean lActivityStartedFlag = ((Boolean) lSession.getAttribute(ACTIVITY_STARTED_FLAG_KEY)).booleanValue();
			if (lActivityStartedFlag) {
				try {
					AdapterFactory lAdapterFac = (AdapterFactory) lSession.getAttribute(ADAPTER_FAC_KEY);
					while (true) {
						if (lAdapterFac.canStop()) {
							if ((lPc != null) && (lPc.getApplicationClass() != null)
									&& !lPc.getApplicationClass().equals(lCanNameGenericUI)) {
								lAdapterFac.stop();
							}
							lAdapterFac.exit();
							break;
						}
					}
					lSession.setAttribute(ADAPTER_FAC_KEY, lAdapterFac);
				} catch (Exception e) {
					sLogger.warn("Could not exit the adapter handling - " + e);
				}
			}

			// Clear ProcessContext
			if (lPc != null) {
				lPc.clearContext();
				lSession.setAttribute(PC_KEY, lPc);
			}
		}
	}

	/**
	 * For getting properties
	 * 
	 * @param pKey
	 *            key which is specified in the properties file
	 * @return Property
	 */
	private String getProperty(String pKey) {
		String lProperty = null;

		try {
			File lPropFile = this.getFile(GwtRemoteServiceConstants.PROPERTY_AGIL_CLIENT_WEB);
			FileInputStream lPropInFile = new FileInputStream(lPropFile);
			Properties lProp = new Properties();
			lProp.load(lPropInFile);

			lProperty = lProp.getProperty(pKey);

			lPropInFile.close();
		} catch (FileNotFoundException e) {
			sLogger.error("Properties could not be loaded: " + e);
		} catch (IOException e) {
			sLogger.error("Properties could not be loaded: " + e);
		}

		return lProperty;
	}

	/**
	 * This method is used when a file of the WEB-INF directory should be loaded
	 * 
	 * @param pFileName
	 * @return File-Object
	 */
	private File getFile(String pFileName) {
		File lFile;

		ServletContext lSc = getServletContext();
		lFile = new File(lSc.getRealPath("/WEB-INF") + pFileName);

		return lFile;
	}

	/**
	 * The flag is true when no Process is currently running
	 * 
	 * @return StopPollingFlag
	 */
	public boolean getStopPollingFlag() {
		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		return ((Boolean) this.getThreadLocalRequest().getSession().getAttribute(STOP_POLLING_FLAG_KEY)).booleanValue();
	}
	
	/**
	 * Returns the proxy to invoke the ClientEjb
	 * 
	 * @return proxy to invoke the ClientEjb
	 */
	private GwtToClientEjbProxy getGwtToClientEjbProxy()	{
		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		return (GwtToClientEjbProxy)this.getThreadLocalRequest().getSession().getAttribute(GWT_TO_CLIENT_EJB_PROXY);
	}
	
	/**
	 * Sets the proxy which should be used to invoke the ClientEjb
	 * 
	 * @param pProxy Proxy
	 */
	private void setGwtToClientEjbProxy(GwtToClientEjbProxy pProxy)	{
		/* 
		 * Only one instance of the servlet is created for all clients.
		 * HttpSession is used to store client specific data
		 */
		this.getThreadLocalRequest().getSession().setAttribute(GWT_TO_CLIENT_EJB_PROXY, pProxy);
	}
}